﻿using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace CloudTrace
{
    [CLSCompliant(false)]
    [Serializable()]
    public class LogMessageEntity : LogMessage, ITableEntity
    {

        public LogMessageEntity() { }

        public LogMessageEntity(LogMessage message) :
            base(message.Source, message.traceEventType, message.EventId, message.eventCache, message.traceOutputOptions, message.Message)
        {
            this.PartitionKey = LogMessage.deploymentId;
            this.RowKey = (DateTime.MaxValue.Ticks - base.Timestamp.Ticks).ToString("d19") + "_" + Guid.NewGuid().ToString(); ;
        }

        public string PartitionKey { get; set; }
        public string RowKey { get; set; }

        public string ETag { get; set; }

        public new DateTimeOffset Timestamp
        {
            get
            {
                return new DateTimeOffset(base.Timestamp);
            }
            set
            {
                base.Timestamp = value.UtcDateTime;
            }
        }

        public void ReadEntity(IDictionary<string, EntityProperty> properties, OperationContext operationContext)
        {
            foreach (PropertyInfo info in base.GetType().GetProperties())
            {
                EntityProperty property;
                if (info.Name == "PartitionKey" || info.Name == "RowKey" || info.Name == "Timestamp" || info.Name != "ETag")
                    continue;
                if (info.GetSetMethod() == null || !info.GetSetMethod().IsPublic || info.GetGetMethod() == null || !info.GetGetMethod().IsPublic
                     || !properties.ContainsKey(info.Name))
                    continue;
                {

                    property = properties[info.Name];

                    bool isNull = (bool)typeof(EntityProperty).GetProperty("IsNull", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(property, null);
                    if (isNull)
                    {
                        info.SetValue(this, null, null);
                    }
                    else
                    {
                        switch (property.PropertyType)
                        {
                            case EdmType.String:
                                {
                                    if (info.PropertyType == typeof(string))
                                    {
                                        info.SetValue(this, property.StringValue, null);
                                    }
                                    break;
                                }
                            case EdmType.Binary:
                                {
                                    if (info.PropertyType == typeof(byte[]))
                                    {
                                        info.SetValue(this, property.BinaryValue, null);
                                    }
                                    break;
                                }
                            case EdmType.Boolean:
                                {
                                    if (info.PropertyType == typeof(bool) || info.PropertyType == typeof(bool?))
                                    {
                                        info.SetValue(this, property.BooleanValue, null);
                                    }
                                    break;
                                }
                            case EdmType.DateTime:
                                {
                                    if (info.PropertyType == typeof(DateTime))
                                    {
                                        info.SetValue(this, property.DateTimeOffsetValue.Value.UtcDateTime, null);
                                    }
                                    else if (info.PropertyType == typeof(DateTime?))
                                    {
                                        info.SetValue(this, property.DateTimeOffsetValue.HasValue ? property.DateTimeOffsetValue.Value.UtcDateTime : (DateTime?)null, null);
                                    }
                                    else if (info.PropertyType == typeof(DateTimeOffset))
                                    {
                                        info.SetValue(this, property.DateTimeOffsetValue.Value, null);
                                    }
                                    else if (info.PropertyType == typeof(DateTimeOffset?))
                                    {
                                        info.SetValue(this, property.DateTimeOffsetValue, null);
                                    }
                                    break;
                                }
                            case EdmType.Double:
                                {
                                    if (info.PropertyType == typeof(double) || info.PropertyType == typeof(double?))
                                    {
                                        info.SetValue(this, property.DoubleValue, null);
                                    }
                                    break;
                                }
                            case EdmType.Guid:
                                {
                                    if ((info.PropertyType == typeof(Guid)) || (info.PropertyType == typeof(Guid?)))
                                    {
                                        info.SetValue(this, property.GuidValue, null);
                                    }
                                    break;
                                }
                            case EdmType.Int32:
                                {
                                    if (info.PropertyType == typeof(int) || info.PropertyType == typeof(int?))
                                    {
                                        info.SetValue(this, property.Int32Value, null);
                                    }
                                    break;
                                }
                            case EdmType.Int64:
                                {
                                    if (info.PropertyType == typeof(long) || info.PropertyType == typeof(long?))
                                    {
                                        info.SetValue(this, property.Int64Value, null);
                                    }
                                    break;
                                }
                        }
                    }
                }
            }
        }

        public IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
        {
            Dictionary<string, EntityProperty> dictionary = new Dictionary<string, EntityProperty>();
            foreach (PropertyInfo info in base.GetType().GetProperties())
            {
                if (((!(info.Name == "PartitionKey") && !(info.Name == "RowKey")) && (!(info.Name == "Timestamp") && !(info.Name == "ETag")))
                    && (((info.GetSetMethod() != null) && info.GetSetMethod().IsPublic) && ((info.GetGetMethod() != null) && info.GetGetMethod().IsPublic)))
                {
                    EntityProperty property = LogMessageEntity.CreateEntityPropertyFromObject(info.GetValue(this, null), false);
                    if (property != null)
                    {
                        dictionary.Add(info.Name, property);
                    }
                }
            }
            return dictionary;
        }

        private static EntityProperty CreateEntityPropertyFromObject(object value, bool allowUnknownTypes)
        {
            if (value is string)
            {
                return new EntityProperty((string)value);
            }
            if (value is byte[])
            {
                return new EntityProperty((byte[])value);
            }
            if (value is bool)
            {
                return new EntityProperty((bool)value);
            }
            if (value is bool?)
            {
                return new EntityProperty((bool?)value);
            }
            if (value is DateTime)
            {
                return new EntityProperty((DateTime)value);
            }
            if (value is DateTime?)
            {
                return new EntityProperty((DateTime?)value);
            }
            if (value is DateTimeOffset)
            {
                return new EntityProperty((DateTimeOffset)value);
            }
            if (value is DateTimeOffset?)
            {
                return new EntityProperty((DateTimeOffset?)value);
            }
            if (value is double)
            {
                return new EntityProperty((double)value);
            }
            if (value is double?)
            {
                return new EntityProperty((double?)value);
            }
            if (value is Guid?)
            {
                return new EntityProperty((Guid?)value);
            }
            if (value is Guid)
            {
                return new EntityProperty((Guid)value);
            }
            if (value is int)
            {
                return new EntityProperty((int)value);
            }
            if (value is int?)
            {
                return new EntityProperty((int?)value);
            }
            if (value is long)
            {
                return new EntityProperty((long)value);
            }
            if (value is long?)
            {
                return new EntityProperty((long?)value);
            }
            if (value == null)
            {
                return new EntityProperty((string)null);
            }
            if (allowUnknownTypes)
            {
                return new EntityProperty(value.ToString());
            }
            return null;
        }
    }
}
